#include <cmath>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/Text.hpp>
#include <nice2d/util.h>
#include <nice2d/misc.h>
#include <nice2d/application.h>
#include <nice2d/components/fontmanager.h>
#include <nice2d/charts/attributechart.h>

class AttributeChartData
{
public:
    AttributeChartData()
    {
        labels.resize(5);
    }

    void update(AttributeChart *chart)
    {
        dirty = false;
        background.clear();
        attribute.clear();

        auto minPosition = sf::Vector2f(std::numeric_limits<float>::max(),
                                        std::numeric_limits<float>::max());

        auto maxPosition = sf::Vector2f(std::numeric_limits<float>::min(),
                                        std::numeric_limits<float>::min());

        float delta = 0.0f;

        size -= 18.0f;

        auto center = getRectCenter(chart->getBoundingBox());

        int32_t count = attributes.size();
        auto fn = [&](int32_t i)->void {
            float angle = i * 360.0f / count - 90.0f;

            float value = attributes[i];
            value = value / 100.0f * size;
            float x = value * std::cos(angle * TO_RADIAN) + position.x;
            float y = value * std::sin(angle * TO_RADIAN) + position.y;

            sf::Vertex v;
            v.position = sf::Vector2f(x, y);
            v.color = verticesColor[i];

            attribute.append(v);

            x = size * std::cos(angle * TO_RADIAN) + position.x;
            y = size * std::sin(angle * TO_RADIAN) + position.y;

            v.position = sf::Vector2f(x, y);
            v.color = backgroundColor;
            background.append(v);

            sf::Vector2f labelPosition = v.position;
            auto box = labels.at(i).getLocalBounds();
            //auto boxPosition = sf::Vector2f(box.left, box.top);

            if (labelPosition.y < center.y)
                labelPosition.y -= 0.5f * box.height;
            else
                labelPosition.y += 0.5f * box.height;

            labelPosition.x -= 0.5f * box.width;

            labels.at(i).setPosition(labelPosition);

            minPosition.x = std::min(minPosition.x, x);
            minPosition.y = std::min(minPosition.y, y);

            maxPosition.x = std::max(maxPosition.x, x);
            maxPosition.y = std::max(maxPosition.y, y);

            delta = chart->getSize().y + chart->getPosition().y -  maxPosition.y - (minPosition.y - chart->getPosition().y);
            delta *= 0.5f;

        };

        sf::Vertex v;
        v.position = position;
        v.color = backgroundColor;

        for (auto i = 1; i < count; i++) {
            fn(i - 1);
            fn(i);
            background.append(v);
            attribute.append(v);
        }

        fn(0);
        fn(count - 1);
        background.append(v);
        attribute.append(v);

        for (size_t i = 0; i < background.getVertexCount(); i++) {
            background[i].position.y = background[i].position.y + delta;
            attribute[i].position.y = attribute[i].position.y + delta;
        }
    }

    std::vector<sf::Color> verticesColor = {sf::Color::Red, sf::Color::Green, sf::Color::Yellow, sf::Color::White, sf::Color::Blue, sf::Color(255, 0, 255, 255)};
    sf::Vector2f position = sf::Vector2f(300, 300);
    float size = 100.0f;
    sf::Color backgroundColor;
    sf::VertexArray background;
    sf::VertexArray attribute;

    std::vector<int32_t> attributes;
    std::vector<sf::Text> labels;

    bool dirty = true;
};

AttributeChart::AttributeChart():
    data(new AttributeChartData())
{
    data->background.setPrimitiveType(sf::Triangles);
    data->attribute.setPrimitiveType(sf::Triangles);
}

AttributeChart::~AttributeChart()
{
}

void AttributeChart::setChartBackground(const sf::Color &background)
{
    data->backgroundColor = background;
}

sf::Color AttributeChart::getChartBackground()const
{
    return data->backgroundColor;
}

void AttributeChart::setFont(const std::string &file, int32_t size)
{
    auto mgr = ADD_COMPONENT(Application::getInstance(), FontManager);
    auto font = mgr->loadFromFile(file);

    if (font) {
        auto itr = data->labels.begin();
        while (itr != data->labels.end()) {
            (*itr).setFont(*font);
            (*itr).setCharacterSize(size);
            itr ++;
        }
    }
}

void AttributeChart::setLables(const std::array<sf::String, 5> &labels)
{
    for (int i = 0; i < 5; i++) {
        data->labels[i].setString(labels[i]);
        data->labels[i].setFillColor(data->verticesColor[i]);
    }
}

void AttributeChart::setData(const std::array<int32_t, 5> &attributes)
{
    data->dirty = true;
    data->attributes.clear();
    std::copy(attributes.begin(), attributes.end(), std::back_inserter(data->attributes));
}

void AttributeChart::onSizeChanged()
{
    data->position = getRectCenter(getBoundingBox());

    auto size = getSize();
    data->size = std::min(size.x, size.y) * 0.5;

    data->dirty = true;
}

void AttributeChart::onPositionChanged()
{
    onSizeChanged();
}

void AttributeChart::onUpdateEntity(float deltaTime)
{
    Entity::onUpdateEntity(deltaTime);

    if (data->dirty) {
        data->update(this);
    }
}

void AttributeChart::onDrawNode(sf::RenderTarget &target, sf::RenderStates states) const
{
    Entity::onDrawNode(target, states);

    sf::RenderStates newStates;
    target.draw(data->background, newStates);
    target.draw(data->attribute, newStates);

    for (const sf::Text &text : data->labels)
        target.draw(text, newStates);
}
